package com.gitee.wsl.collections.map

import com.gitee.wsl.base.Objects
import com.gitee.wsl.collections.delegate.DelegatingMap
import com.gitee.wsl.collections.delegate.DelegatingMutableMap
import com.gitee.wsl.func.factory.Factory
import com.gitee.wsl.func.transform.Transformer
import com.gitee.wsl.func.transform.sample.FactoryTransformer
import kotlinx.io.IOException


/**
 * Decorates another `Map` to create objects in the map on demand.
 *
 *
 * When the [.get] method is called with a key that does not
 * exist in the map, the factory is used to create the object. The created
 * object will be added to the map using the requested key.
 *
 *
 *
 * For instance:
 *
 * <pre>
 * Factory&lt;Date&gt; factory = new Factory&lt;Date&gt;() {
 * public Date create() {
 * return new Date();
 * }
 * }
 * Map&lt;String, Date&gt; lazy = LazyMap.lazyMap(new HashMap&lt;String, Date&gt;(), factory);
 * Date date = lazy.get("NOW");
</pre> *
 *
 *
 *
 * After the above code is executed, `date` will refer to
 * a new `Date` instance. Furthermore, that `Date`
 * instance is mapped to the "NOW" key in the map.
 *
 *
 *
 * **Note that LazyMap is not synchronized and is not thread-safe.**
 * If you wish to use this map from multiple threads concurrently, you must use
 * appropriate synchronization. The simplest approach is to wrap this map
 * using [java.util.Collections.synchronizedMap]. This class may throw
 * exceptions when accessed by concurrent threads without synchronization.
 *
 *
 *
 * This class is Serializable from Commons Collections 3.1.
 *
 *
 * @param <K> the type of the keys in this map
 * @param <V> the type of the values in this map
 * @since 3.0
</V></K> */
class LazyMap<K, V> : DelegatingMutableMap<K, V> {
    /** The factory to use to construct elements  */
    protected val factory: Transformer<in K, out V>

    /**
     * Constructor that wraps (not copies).
     *
     * @param map  the map to decorate, must not be null
     * @param factory  the factory to use, must not be null
     * @throws NullPointerException if map or factory is null
     */
    protected constructor(map: MutableMap<K,V>, factory: Factory<V>) : super(map) {
        this.factory = FactoryTransformer.factoryTransformer(factory)
    }

    /**
     * Constructor that wraps (not copies).
     *
     * @param map  the map to decorate, must not be null
     * @param factory  the factory to use, must not be null
     * @throws NullPointerException if map or factory is null
     */
    protected constructor(map: MutableMap<K,V>, factory: Transformer<in K, out V>) : super(map) {
        this.factory = factory
    }

     override fun get(key: K): V? {
        // create value for key if key is not currently in the map
        if (!delegate.containsKey(key)) {
            val castKey = key
            val value = factory.apply(castKey)
            delegate.put(castKey, value)
            return value
        }
        return delegate[key]
    }

    /**
     * Deserializes the map in using a custom routine.
     *
     * @param in  the input stream
     * @throws IOException if an error occurs while reading from the stream
     * @throws ClassNotFoundException if an object read from the stream cannot be loaded
     * @since 3.1
     */
//    @Throws(IOException::class, ClassNotFoundException::class)
//    private fun readObject(`in`: ObjectInputStream) {
//        `in`.defaultReadObject()
//        map = `in`.readObject() as Map<K,V>?
//    }

    /**
     * Serializes this object to an ObjectOutputStream.
     *
     * @param out the target ObjectOutputStream.
     * @throws IOException thrown when an I/O errors occur writing to the target stream.
     * @since 3.1
     */
//    @Throws(IOException::class)
//    private fun writeObject(out: ObjectOutputStream) {
//        out.defaultWriteObject()
//        out.writeObject(map)
//    } // no need to wrap keySet, entrySet or values as they are views of
    // existing map entries - you can't do a map-style get on them.

    companion object {
        /** Serialization version  */
        private const val serialVersionUID = 7990956402564206740L

        /**
         * Factory method to create a lazily instantiated map.
         *
         * @param <K>  the key type
         * @param <V>  the value type
         * @param map  the map to decorate, must not be null
         * @param factory  the factory to use, must not be null
         * @return a new lazy map
         * @throws NullPointerException if map or factory is null
         * @since 4.0
        </V></K> */
        fun <K, V> lazyMap(map: MutableMap<K,V>, factory: Factory< V>): LazyMap<K,V> {
            return LazyMap<K,V>(map, factory)
        }

        /**
         * Factory method to create a lazily instantiated map.
         *
         * @param <K>  the key type
         * @param <V>  the value type
         * @param map  the map to decorate, must not be null
         * @param factory  the factory to use, must not be null
         * @return a new lazy map
         * @throws NullPointerException if map or factory is null
         * @since 4.0
        </V></K> */
        fun <V, K> lazyMap(map: MutableMap<K,V>, factory: Transformer<in K, out V>): LazyMap<K,V> {
            return LazyMap<K,V>(map, factory)
        }
    }
}
